/*
Copyright (C) Ariff Ehsan, Abdul Hadi Fikri

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/

/*  Author : Ariff Ehsan
 *  Date   : 28 June 2009
 */


#include <system.h>


enum ir_state_enums {
	IR_STOP = 0,
	IR_IDLE,
	IR_READ_OFFSET,
	IR_READ_SENSOR,
};


/* variables */
uint8_t ir_state = IR_STOP;
uint8_t ir_pin;

uint16_t ir_offset;

void (*ir_readdone)(uint16_t);

task_t ir_task;


/* prototype function */
void IR_ADC_readDone(uint16_t);
void IR_defaultReadDone(uint16_t);
void IR_TASK_run(void);


void IR_init(void){	
	
	/* initialize ir variables */
	ir_state = IR_IDLE;
	ir_readdone = IR_defaultReadDone;
	
	/* initialize wire adc */
	ADC_init();
	ADC_reg(IR_ADC_readDone);
	
	/* register task event */
	TASK_reg(&ir_task, IR_TASK_run);
	
	/* configure multiplexer output pins */
	DDRB |= (1<<PB2)|(1<<PB3)|(1<<PB4);
	SET_BIT(DDRD, PD7);
	
	/* disable multiplexer output */
	SET_BIT(PORTD, PD7);
}


void IR_reg(void (*event)(uint16_t result)) {
	if (event != NULL)
		ir_readdone = event;
}


void IR_read(uint8_t pin){
	
	if (ir_state != IR_IDLE)
		return;
	
	ir_state = IR_READ_OFFSET;
	ir_pin = pin;
	
	/* start 150us delay using timer2 */
	TCCR2A = (1<<WGM21);
	TCCR2B = (1<<CS21); // prescaler 8
	TCNT2 = 0;
	OCR2A = 150;
		
	/* start polling OCF2A */
	TASK_post(&ir_task);
	
}


void IR_ADC_readDone(uint16_t result) {
	
	switch (ir_state) {
	case IR_READ_OFFSET:
		ir_state = IR_READ_SENSOR;
		ir_offset = result;
		
		/* set output multiplexer */
		PORTB &= 0xe3;
		PORTB |= (ir_pin<<2);
		
		/* enable transmitter */
		CLR_BIT(PORTD, PD7);
		
		/* start 150us delay using timer2 */
		TCCR2A = (1<<WGM21);
		TCCR2B = (1<<CS21); // prescaler 8
		TCNT2 = 0;
		OCR2A = 150;
		
		/* start polling OCF2A */
		TASK_post(&ir_task);
		
		break;
		
	case IR_READ_SENSOR:
		ir_state = IR_IDLE;
		
		/* disable transmitter */
		SET_BIT(PORTD, PD7);
		
		(*ir_readdone)(ir_offset - result);
	}
	
}


void IR_TASK_run(void) {
	
	if (READ_BIT(TIFR2, OCF2A)) {
		/* reset OCF2A flag and disable timer2 */
		SET_BIT(TIFR2, OCF2A);
		TCCR2B = 0;
		
		/* read sensor */
		ADC_read(7-ir_pin);
	}
	else {
		TASK_post(&ir_task);
	}
}


void IR_defaultReadDone(uint16_t result) {
}
